__author__ = 'Muscle'
"""@package Widgets.panel
The Panel controls

"""

from mui.muirectangle import Rectangle
from mui.Widgets.layout import LayoutBase
from mui.muicomponent import MUIComponent, CaptureObject, clamp
import mui.muianim as Anim
import traceback




class Panel(MUIComponent, LayoutBase):
    """Panel is a base class for all panel layout
    """
    def __init__(self, parent=None, **argd):
        MUIComponent.__init__(self, parent, **argd)
        LayoutBase.__init__(self)
        self.tabStop = False


class MUIStackPanel(Panel):
    """Stack style layout panel
    """
    def __init__(self, parent=None, **argd):
        Panel.__init__(self, parent, **argd)
        self.orientation = argd.get('orientation', 'Vertical')

        self.margin = argd.get('margin', (0, 0))
        self.scrollDuration = 0.3
        self.scrollOffset = 0
        self.scrollTarget = 0

        self.autosize = argd.get('autosize', False)   # True: panel height (vertical mode) will auto fit with sum of children size
        self.autofit =  argd.get('autofit', False)   # True: panel width will fit into maximun of children
        self.center = argd.get('center', False)   # True: child will put in center, False: child will fit to stackpanel
        self.fullSize = 10 # will be update in relayout
        self.changeEvent('dirty', self.__onDirty)

        if argd.get('autofix', True):     # True: fullSize change will auto fix position
            self.changeEvent('fullSize', self.__onFullSizeChange)
        self.autoDirty('scrollOffset')
        self.autoLayout(['orientation', 'margin', 'autosize', 'autofit', 'scrollOffset'], [])

    def passQueryDrop(self, x, y, state, *arg):
        """
        pass query drop message to children, and then process the query message

        @rtype: True if mouse over myself or one of my children
        """
        if not self.visible:
            return False

        for c in self.children[::-1]:
            if c.visible and not c.blind:

                # In some situations (scroll in StackPanel), local coordinate has been translated by scrolling or others.
                # And hittest need the original rect

                ptx, pty = c.parent2local(x, y)
                _x, _y = Component.parent2local(c, x, y)

                if c.hitTest(_x, _y):
                    ret = c.passQueryDrop(ptx, pty, state, *arg)
                    if ret:
                        return ret
        return self.handleQueryDrop(x, y, state, *arg)

    def passMouseMsg(self, msgType, x, y, **kwargs):
        """
        pass mouse message, and then process the mouse message
        @param x: mouse pos x in local coordinate
        @param y: mouse pos y in local coordinate
        @rtype: True if mouse over myself or one of my children
        """
        if MUIComponent.mouseCapture is not self:
            '''
            In some situations (scroll in StackPanel), local coordinate has been translated by scrolling or others.
            And hittest need the original rect
            '''
            _x, _y = self.local2parent(x, y)
            if not self.hitTest(*MUIComponent.parent2local(self, _x, _y)):
            #if not self.hitTest(x, y):
                return False
            if not self.enabled:
                return self.handleMouseMsg(msgType, x, y, **kwargs)   # control is disabled, so mouse event should stop here
            if self.passMouseMsgToChildren(msgType, x, y, **kwargs):
                return True # mouse pass to child
        return self.handleMouseMsg(msgType, x, y, **kwargs)

    def __onDirty(self):
        self.allSightDirty = True

    def __getWindowPosition(self):
        return self.local2window(-self.margin[0], -self.margin[1]) + self.local2window(self.width - self.margin[0], self.height - self.margin[1])
    windowPosition = property(__getWindowPosition)


    def drawChild(self, renderer):
        renderer.PushAxisAlignedClip((self.margin[0], self.margin[1], self.width - self.margin[0], self.height - self.margin[1]))
        self.drawViewChild(renderer)
        renderer.PopAxisAlignedClip()

    def drawViewChild(self, renderer):
        super(MUIStackPanel, self).drawChild(renderer)

    def onScroll(self, offset):
        view = self.viewSize
        full = self.fullSize
        if full > view:
            offset = clamp(0, offset, full-view)
            curScrollOffset = self.scrollOffset
            if (self.scrollTarget == offset):
                return
            self.scrollTarget = offset
            animobj = Anim.AnimDecay([0, 1.0], [curScrollOffset, offset], self.scrollDuration)
            animTask = Anim.AnimTask(self, 'scrollOffset',animobj, bAllowInterrupt = True)
            animTask.start()

    def __getViewSize(self):
        return self.height if self.orientation == 'Vertical' else self.width
    viewSize = property(__getViewSize)

    def __onFullSizeChange(self):
        '''
        full size is change, we should reset the position
        '''
        offset = float(self.scrollOffset)
        if self.fullSize < self.viewSize:
            curScrollOffset = self.scrollOffset
            animobj = Anim.AnimDecay([0, 1.0], [curScrollOffset, 0], self.scrollDuration)
            animTask = Anim.AnimTask(self, 'scrollOffset',animobj, bAllowInterrupt = True)
            animTask.start()
        elif (offset + self.viewSize) > self.fullSize:
            curScrollOffset = self.scrollOffset
            animobj = Anim.AnimDecay([0, 1.0], [curScrollOffset, self.fullSize - self.viewSize], self.scrollDuration)
            animTask = Anim.AnimTask(self, 'scrollOffset',animobj, bAllowInterrupt = True)
            animTask.start()

    #@staticmethod
    def verticalLayout(self, maxHeight=None):
        top = -self.scrollOffset + self.margin[1]
        children = self._children
        if self.autofit:
            for i in children:
                if i.visible:
                    self.width = max(self.width, i.width + self.margin[0] * 2)

        childTotalHeight = 0
        for i in children:
            if i.visible:
                childTotalHeight += i.height
                i.top = top
                if self.center:
                    i.left = (self.width - i.width) / 2.0 - self.margin[0]
                else:
                    i.width = self.width - self.margin[0] * 2
                    i.left = self.margin[0]
                top += i.height
        self.fullSize = childTotalHeight + self.margin[1] * 2
        if self.autosize:
            if maxHeight is None:
                self.height = self.fullSize
            else:
                self.height = min(self.fullSize, maxHeight)

        self.setDirty(True)

    #@staticmethod
    def horizontalLayout(self):
        left = -self.scrollOffset + self.margin[0]
        children = self._children
        if self.autofit:
            for i in children:
                if i.visible:
                    self.height = max(self.height, i.height + self.margin[1] * 2)

        childTotalWidth = 0
        for i in children:
            if i.visible:
                childTotalWidth += i.width
                i.left = left
                if self.center:
                    i.top = (self.height - i.height) / 2.0 - self.margin[1]
                else:
                    i.height = self.height - self.margin[1] * 2
                    i.top = self.margin[1]
                left += i.width

        self.fullSize = childTotalWidth + self.margin[0] * 2
        if self.autosize:
            self.width = self.fullSize
        self.setDirty(True)


    def relayout(self):
        if self.orientation == 'Vertical':
            self.verticalLayout()
        else:
            self.horizontalLayout()


class MUIDockSplitter(MUIComponent, CaptureObject):
    """DockSplitter is a control used only with DockPanel which let you to relayout UI interactively.
    """
    def __init__(self, parent=None, **argd):
        argd.setdefault('tabStop', False)
        MUIComponent.__init__(self, parent, **argd)
        CaptureObject.__init__(self)
        self.useGlobalCapture = True

        self.bind('Mouse Enter', self.onChangeCursor, True, noarg = True)
        self.bind('Mouse Leave', self.onChangeCursor, False, noarg = True)
        if self.parent:
            self.parent.autoRemove( self.bind('Capture Begin', self.__onCaptureBegin, postevent = False))
            self.parent.autoRemove( self.bind('Capture Offset', self.__onCaptureEnd, postevent = False))

    def __onCaptureBegin(self, x, y):
        try:
            self.parent.invoke('Splitter Start', self, x, y)
        except Exception, e:
            traceback.print_exc()
            return None

    def __onCaptureEnd(self, x, y):
        try:
            self.parent.invoke('Splitter Move', self, x, y)
        except Exception, e:
            traceback.print_exc()
            return None
        
    def onChangeCursor(self, v):
        if not self.window.dragDroping:
            if v and hasattr(self, '_dock_'):
                if self._dock_ in ['left', 'right']:
                    self.window.SetCursor(unicode('size we'))
                else:
                    self.window.SetCursor(unicode('size ns'))
            else:
                self.window.SetCursor(unicode('arrow'))


class MUIDockPanel(Panel):
    """Dock style layout panel
    """
    def __init__(self, parent=None, **argd):
        Panel.__init__(self, parent, **argd)
        self.defaultDock = 'top'
        self.lastFill = True

        self.bind('Splitter Start', self.onSplitterStart)
        self.bind('Splitter Move', self.onSplitterSize)

    def onSplitterStart(self, splt, _x, _y):
        assert splt
        try:
            _children = [c for c in self._children if c.visible]
            idx = _children.index(splt)
            o = _children[idx-1]
            splt.orgWidth = o.width
            splt.orgHeight = o.height
            #print 'Splitter start', split.orgWidth, split.orgHeight
        except Exception, e:
            traceback.print_exc()
            return None

    def onSplitterSize(self, splt, x, y):
        assert splt
        try:
            _children = [c for c in self._children if c.visible]
            idx = _children.index(splt)
            o = _children[idx-1]
            if o._dock_ in ('left', 'right'):
                if o._dock_ == 'left':
                    o.width = max(0, splt.orgWidth + x)
                else:
                    o.width = max(0, splt.orgWidth - x)

                if hasattr(o, 'maxWidth'):
                    o.width = min(o.width, o.maxWidth)
                if hasattr(o, 'minWidth'):
                    o.width = max(o.width, o.minWidth)
            elif o._dock_ in ('top', 'bottom'):
                if o._dock_ == 'top':
                    o.height = max(0, splt.orgHeight + y)
                else:
                    o.height = max(0, splt.orgHeight - y)

                if hasattr(o, 'maxHeight'):
                    o.height = min(o.height, o.maxHeight)
                if hasattr(o, 'minHeight'):
                    o.height = max(o.height, o.minHeight)
        except Exception, e:
            traceback.print_exc()
            return None

    def relayout(self):
        r = Rectangle(*self.localRect)

        _children = [c for c in self._children if c.visible]

        for c in _children[:-1]:
            if isinstance(c, MUIDockSplitter):
                idx = _children.index(c)
                if idx > 0:
                    c._dock_ = _children[idx-1]._dock_
            elif not hasattr(c, '_dock_'):
                c._dock_ = self.defaultDock        # set default to top

            if c._dock_ == 'left':
                c.height, c.top, c.left = r.height, r.top, r.left
                r.innerOffset(c.width, 0, 0, 0)
            elif c._dock_ == 'right':
                c.height, c.top, c.left = r.height, r.top, r.right - c.width
                r.innerOffset(0, 0, c.width, 0)
            elif c._dock_ == 'top':
                c.width, c.left, c.top = r.width, r.left, r.top
                r.innerOffset(0, c.height, 0, 0)
            elif c._dock_ == 'bottom':
                c.width, c.left, c.top = r.width, r.left, r.bottom - c.height
                r.innerOffset(0, 0, 0, c.height)
        #------------------------------------------
        # last object must fill the whole panel
        #------------------------------------------
        if _children:
            _children[-1].rect = r.rect
        self.setDirty(True)

    def dock(self, obj, side):
        obj._dock_ = side

    def setAttachedProperty(self, prop, **_argd):
        name = prop['name'].split('.')[-1]
        side = prop['data']
        obj = prop['child']
        if name.lower() == 'dock':
            side = side.strip().lower()
            if side not in ['left', 'top', 'right', 'bottom']:
                side = self.defaultDock
            self.dock(obj, side)
